package cn.ffcs.util.hibernate.generator;

import java.util.Properties;
import java.io.Serializable;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.hibernate.mapping.Table;
import org.hibernate.LockMode;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.type.Type;
import org.hibernate.dialect.Dialect;
import org.hibernate.id.*;
import org.hibernate.util.PropertiesHelper;

public class MyGenerator extends TableGenerator {

	private static final Logger log = LoggerFactory
			.getLogger(MyGenerator.class);

	public static final String SEQUENCENAME = "sequence_name";
	public static final String SEQUENCECOLUME = "sequence_column";

	private String tableName;
	private String columnName;
	private String sequenceColume;
	private String query;
	private String update;

	private String sequenceName;

	public void configure(Type type, Properties params, Dialect dialect) {

		tableName = PropertiesHelper.getString(TABLE, params,
				DEFAULT_TABLE_NAME);
		columnName = PropertiesHelper.getString(COLUMN, params,
				DEFAULT_COLUMN_NAME);
		sequenceName = PropertiesHelper.getString(SEQUENCENAME, params, "");
	
		sequenceColume = PropertiesHelper.getString(SEQUENCECOLUME, params, "");

		String schemaName = params.getProperty(SCHEMA);
		String catalogName = params.getProperty(CATALOG);

		if (tableName.indexOf('.') < 0) {
			tableName = Table.qualify(catalogName, schemaName, tableName);
		}

		query = "select " + columnName + " from "
				+ dialect.appendLockHint(LockMode.UPGRADE, tableName)
				+ " where " + sequenceColume + "='" + sequenceName + "'"
				+ dialect.getForUpdateString();

		update = "update " + tableName + " set " + columnName + " = ? where "
				+ sequenceColume + "= '" + sequenceName + "'";

	}

	public Serializable doWorkInCurrentTransaction(Connection conn, String sql)
			throws SQLException {
		int result;
		int rows;
		do {
			// The loop ensures atomicity of the
			// select + update even for no transaction
			// or read committed isolation level

			sql = query;
			SQL_STATEMENT_LOGGER.logStatement(sql, FormatStyle.BASIC);
			PreparedStatement qps = conn.prepareStatement(query);
			try {
				ResultSet rs = qps.executeQuery();
				if (!rs.next()) {
					String err = "could not read a hi value - you need to populate the table: "
							+ tableName;
					log.error(err);
					throw new IdentifierGenerationException(err);
				}
				result = rs.getInt(1);
				rs.close();
			} catch (SQLException sqle) {
				log.error("could not read a hi value", sqle);
				throw sqle;
			} finally {
				qps.close();
			}

			sql = update;
			SQL_STATEMENT_LOGGER.logStatement(sql, FormatStyle.BASIC);
			PreparedStatement ups = conn.prepareStatement(update);
			try {
				ups.setInt(1, result + 1);
				rows = ups.executeUpdate();
			} catch (SQLException sqle) {
				log.error("could not update hi value in: " + tableName, sqle);
				throw sqle;
			} finally {
				ups.close();
			}
		} while (rows == 0);
		return new Integer(result);
	}
}
